/**
 * Copyright (c) 2010 CWI Amsterdam and others. All rights reserved.
 * This program and the accompanying materials are made available
 * under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Contributors:
 *     CWI Amsterdam - initial API and implementation
 *
 * $Id$
 */
package nl.cwi.arne.impl;

import java.util.Collection;

import nl.cwi.arne.Edge;
import nl.cwi.arne.Network;
import nl.cwi.arne.Node;
import nl.cwi.arne.factory.ArnePackage;

import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.NotificationChain;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.InternalEObject;
import org.eclipse.emf.ecore.impl.ENotificationImpl;
import org.eclipse.emf.ecore.impl.MinimalEObjectImpl;
import org.eclipse.emf.ecore.util.EDataTypeUniqueEList;
import org.eclipse.emf.ecore.util.EcoreUtil;

/**
 * <!-- begin-user-doc -->
 * An implementation of the model object '<em><b>Edge</b></em>'.
 * <!-- end-user-doc -->
 * <p>
 * The following features are implemented:
 * <ul>
 *   <li>{@link nl.cwi.arne.impl.EdgeImpl#getSource <em>Source</em>}</li>
 *   <li>{@link nl.cwi.arne.impl.EdgeImpl#getTarget <em>Target</em>}</li>
 *   <li>{@link nl.cwi.arne.impl.EdgeImpl#getNetwork <em>Network</em>}</li>
 *   <li>{@link nl.cwi.arne.impl.EdgeImpl#getBaseLevels <em>Base Levels</em>}</li>
 *   <li>{@link nl.cwi.arne.impl.EdgeImpl#getStartLevel <em>Start Level</em>}</li>
 *   <li>{@link nl.cwi.arne.impl.EdgeImpl#getEndLevel <em>End Level</em>}</li>
 * </ul>
 * </p>
 *
 * @generated
 */
public class EdgeImpl extends MinimalEObjectImpl implements Edge {

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated NOT
	 */
	public boolean isUpClosed() {
		if (getBaseLevels().isEmpty()) return false;
		return (isClosed(getMinBaseLevel(), true));
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated NOT
	 */
	public boolean isDownClosed() {
		if (getBaseLevels().isEmpty()) return false;
		return (isClosed(getMaxBaseLevel()-1,false));
	}
	
	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated NOT
	 */
	public int getMinBaseLevel() {
		int min = getSource().getLevels();
		for (Integer level : getBaseLevels()) {
			if (level<min) min = level;
		}
		return min;
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated NOT
	 */
	public int getMaxBaseLevel() {
		int max = -1;
		for (Integer level : getBaseLevels()) {
			if (level>max) max = level;
		}
		return max;
	}

	/*
	 * Check if the base levels are up/downward closed.
	 */
	private boolean isClosed(int from, boolean upward) {
		if (upward) {
			if (from>=getSource().getLevels()) return true;
			if (!getBaseLevels().contains(from)) return false;
			return isClosed(from+1, upward);
		} else {
			if (from<0) return true;
			if (!getBaseLevels().contains(from)) return false;
			return isClosed(from-1, upward);
		}
	}
	
	/**
	 * @generated NOT
	 */
	@Override
	public String toString() {
		String src = source!=null ? source.getName() : "null";
		String trg = target!=null ? target.getName() : "null";
		return src + "->" + trg; 
	}
	
	/* ---------------------------------------------------------------- *
	 * GENERATED CODE.                                                  *
	 * Do not edit below this line. If you need to edit, move it above  *
	 * this line and change the '@generated'-tag to '@generated NOT'.   *
	 * ---------------------------------------------------------------- */
	
	/**
	 * The cached value of the '{@link #getSource() <em>Source</em>}' reference.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @see #getSource()
	 * @generated
	 * @ordered
	 */
	protected Node source;

	/**
	 * The cached value of the '{@link #getTarget() <em>Target</em>}' reference.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @see #getTarget()
	 * @generated
	 * @ordered
	 */
	protected Node target;

	/**
	 * The cached value of the '{@link #getBaseLevels() <em>Base Levels</em>}' attribute list.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @see #getBaseLevels()
	 * @generated
	 * @ordered
	 */
	protected EList<Integer> baseLevels;

	/**
	 * The default value of the '{@link #getStartLevel() <em>Start Level</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @see #getStartLevel()
	 * @generated
	 * @ordered
	 */
	protected static final int START_LEVEL_EDEFAULT = 0;

	/**
	 * The cached value of the '{@link #getStartLevel() <em>Start Level</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @see #getStartLevel()
	 * @generated
	 * @ordered
	 */
	protected int startLevel = START_LEVEL_EDEFAULT;

	/**
	 * The default value of the '{@link #getEndLevel() <em>End Level</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @see #getEndLevel()
	 * @generated
	 * @ordered
	 */
	protected static final int END_LEVEL_EDEFAULT = 0;

	/**
	 * The cached value of the '{@link #getEndLevel() <em>End Level</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @see #getEndLevel()
	 * @generated
	 * @ordered
	 */
	protected int endLevel = END_LEVEL_EDEFAULT;

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	protected EdgeImpl() {
		super();
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	@Override
	protected EClass eStaticClass() {
		return ArnePackage.Literals.EDGE;
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	public Node getSource() {
		if (source != null && source.eIsProxy()) {
			InternalEObject oldSource = (InternalEObject)source;
			source = (Node)eResolveProxy(oldSource);
			if (source != oldSource) {
				if (eNotificationRequired())
					eNotify(new ENotificationImpl(this, Notification.RESOLVE, ArnePackage.EDGE__SOURCE, oldSource, source));
			}
		}
		return source;
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	public Node basicGetSource() {
		return source;
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	public NotificationChain basicSetSource(Node newSource, NotificationChain msgs) {
		Node oldSource = source;
		source = newSource;
		if (eNotificationRequired()) {
			ENotificationImpl notification = new ENotificationImpl(this, Notification.SET, ArnePackage.EDGE__SOURCE, oldSource, newSource);
			if (msgs == null) msgs = notification; else msgs.add(notification);
		}
		return msgs;
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	public void setSource(Node newSource) {
		if (newSource != source) {
			NotificationChain msgs = null;
			if (source != null)
				msgs = ((InternalEObject)source).eInverseRemove(this, ArnePackage.NODE__OUTGOING, Node.class, msgs);
			if (newSource != null)
				msgs = ((InternalEObject)newSource).eInverseAdd(this, ArnePackage.NODE__OUTGOING, Node.class, msgs);
			msgs = basicSetSource(newSource, msgs);
			if (msgs != null) msgs.dispatch();
		}
		else if (eNotificationRequired())
			eNotify(new ENotificationImpl(this, Notification.SET, ArnePackage.EDGE__SOURCE, newSource, newSource));
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	public Node getTarget() {
		if (target != null && target.eIsProxy()) {
			InternalEObject oldTarget = (InternalEObject)target;
			target = (Node)eResolveProxy(oldTarget);
			if (target != oldTarget) {
				if (eNotificationRequired())
					eNotify(new ENotificationImpl(this, Notification.RESOLVE, ArnePackage.EDGE__TARGET, oldTarget, target));
			}
		}
		return target;
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	public Node basicGetTarget() {
		return target;
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	public NotificationChain basicSetTarget(Node newTarget, NotificationChain msgs) {
		Node oldTarget = target;
		target = newTarget;
		if (eNotificationRequired()) {
			ENotificationImpl notification = new ENotificationImpl(this, Notification.SET, ArnePackage.EDGE__TARGET, oldTarget, newTarget);
			if (msgs == null) msgs = notification; else msgs.add(notification);
		}
		return msgs;
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	public void setTarget(Node newTarget) {
		if (newTarget != target) {
			NotificationChain msgs = null;
			if (target != null)
				msgs = ((InternalEObject)target).eInverseRemove(this, ArnePackage.NODE__INCOMING, Node.class, msgs);
			if (newTarget != null)
				msgs = ((InternalEObject)newTarget).eInverseAdd(this, ArnePackage.NODE__INCOMING, Node.class, msgs);
			msgs = basicSetTarget(newTarget, msgs);
			if (msgs != null) msgs.dispatch();
		}
		else if (eNotificationRequired())
			eNotify(new ENotificationImpl(this, Notification.SET, ArnePackage.EDGE__TARGET, newTarget, newTarget));
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	public Network getNetwork() {
		if (eContainerFeatureID() != ArnePackage.EDGE__NETWORK) return null;
		return (Network)eContainer();
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	public NotificationChain basicSetNetwork(Network newNetwork, NotificationChain msgs) {
		msgs = eBasicSetContainer((InternalEObject)newNetwork, ArnePackage.EDGE__NETWORK, msgs);
		return msgs;
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	public void setNetwork(Network newNetwork) {
		if (newNetwork != eInternalContainer() || (eContainerFeatureID() != ArnePackage.EDGE__NETWORK && newNetwork != null)) {
			if (EcoreUtil.isAncestor(this, newNetwork))
				throw new IllegalArgumentException("Recursive containment not allowed for " + toString());
			NotificationChain msgs = null;
			if (eInternalContainer() != null)
				msgs = eBasicRemoveFromContainer(msgs);
			if (newNetwork != null)
				msgs = ((InternalEObject)newNetwork).eInverseAdd(this, ArnePackage.NETWORK__EDGES, Network.class, msgs);
			msgs = basicSetNetwork(newNetwork, msgs);
			if (msgs != null) msgs.dispatch();
		}
		else if (eNotificationRequired())
			eNotify(new ENotificationImpl(this, Notification.SET, ArnePackage.EDGE__NETWORK, newNetwork, newNetwork));
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	public EList<Integer> getBaseLevels() {
		if (baseLevels == null) {
			baseLevels = new EDataTypeUniqueEList<Integer>(Integer.class, this, ArnePackage.EDGE__BASE_LEVELS);
		}
		return baseLevels;
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	public int getStartLevel() {
		return startLevel;
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	public void setStartLevel(int newStartLevel) {
		int oldStartLevel = startLevel;
		startLevel = newStartLevel;
		if (eNotificationRequired())
			eNotify(new ENotificationImpl(this, Notification.SET, ArnePackage.EDGE__START_LEVEL, oldStartLevel, startLevel));
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	public int getEndLevel() {
		return endLevel;
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	public void setEndLevel(int newEndLevel) {
		int oldEndLevel = endLevel;
		endLevel = newEndLevel;
		if (eNotificationRequired())
			eNotify(new ENotificationImpl(this, Notification.SET, ArnePackage.EDGE__END_LEVEL, oldEndLevel, endLevel));
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	@Override
	public NotificationChain eInverseAdd(InternalEObject otherEnd, int featureID, NotificationChain msgs) {
		switch (featureID) {
			case ArnePackage.EDGE__SOURCE:
				if (source != null)
					msgs = ((InternalEObject)source).eInverseRemove(this, ArnePackage.NODE__OUTGOING, Node.class, msgs);
				return basicSetSource((Node)otherEnd, msgs);
			case ArnePackage.EDGE__TARGET:
				if (target != null)
					msgs = ((InternalEObject)target).eInverseRemove(this, ArnePackage.NODE__INCOMING, Node.class, msgs);
				return basicSetTarget((Node)otherEnd, msgs);
			case ArnePackage.EDGE__NETWORK:
				if (eInternalContainer() != null)
					msgs = eBasicRemoveFromContainer(msgs);
				return basicSetNetwork((Network)otherEnd, msgs);
		}
		return super.eInverseAdd(otherEnd, featureID, msgs);
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	@Override
	public NotificationChain eInverseRemove(InternalEObject otherEnd, int featureID, NotificationChain msgs) {
		switch (featureID) {
			case ArnePackage.EDGE__SOURCE:
				return basicSetSource(null, msgs);
			case ArnePackage.EDGE__TARGET:
				return basicSetTarget(null, msgs);
			case ArnePackage.EDGE__NETWORK:
				return basicSetNetwork(null, msgs);
		}
		return super.eInverseRemove(otherEnd, featureID, msgs);
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	@Override
	public NotificationChain eBasicRemoveFromContainerFeature(NotificationChain msgs) {
		switch (eContainerFeatureID()) {
			case ArnePackage.EDGE__NETWORK:
				return eInternalContainer().eInverseRemove(this, ArnePackage.NETWORK__EDGES, Network.class, msgs);
		}
		return super.eBasicRemoveFromContainerFeature(msgs);
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	@Override
	public Object eGet(int featureID, boolean resolve, boolean coreType) {
		switch (featureID) {
			case ArnePackage.EDGE__SOURCE:
				if (resolve) return getSource();
				return basicGetSource();
			case ArnePackage.EDGE__TARGET:
				if (resolve) return getTarget();
				return basicGetTarget();
			case ArnePackage.EDGE__NETWORK:
				return getNetwork();
			case ArnePackage.EDGE__BASE_LEVELS:
				return getBaseLevels();
			case ArnePackage.EDGE__START_LEVEL:
				return getStartLevel();
			case ArnePackage.EDGE__END_LEVEL:
				return getEndLevel();
		}
		return super.eGet(featureID, resolve, coreType);
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	@SuppressWarnings("unchecked")
	@Override
	public void eSet(int featureID, Object newValue) {
		switch (featureID) {
			case ArnePackage.EDGE__SOURCE:
				setSource((Node)newValue);
				return;
			case ArnePackage.EDGE__TARGET:
				setTarget((Node)newValue);
				return;
			case ArnePackage.EDGE__NETWORK:
				setNetwork((Network)newValue);
				return;
			case ArnePackage.EDGE__BASE_LEVELS:
				getBaseLevels().clear();
				getBaseLevels().addAll((Collection<? extends Integer>)newValue);
				return;
			case ArnePackage.EDGE__START_LEVEL:
				setStartLevel((Integer)newValue);
				return;
			case ArnePackage.EDGE__END_LEVEL:
				setEndLevel((Integer)newValue);
				return;
		}
		super.eSet(featureID, newValue);
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	@Override
	public void eUnset(int featureID) {
		switch (featureID) {
			case ArnePackage.EDGE__SOURCE:
				setSource((Node)null);
				return;
			case ArnePackage.EDGE__TARGET:
				setTarget((Node)null);
				return;
			case ArnePackage.EDGE__NETWORK:
				setNetwork((Network)null);
				return;
			case ArnePackage.EDGE__BASE_LEVELS:
				getBaseLevels().clear();
				return;
			case ArnePackage.EDGE__START_LEVEL:
				setStartLevel(START_LEVEL_EDEFAULT);
				return;
			case ArnePackage.EDGE__END_LEVEL:
				setEndLevel(END_LEVEL_EDEFAULT);
				return;
		}
		super.eUnset(featureID);
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	@Override
	public boolean eIsSet(int featureID) {
		switch (featureID) {
			case ArnePackage.EDGE__SOURCE:
				return source != null;
			case ArnePackage.EDGE__TARGET:
				return target != null;
			case ArnePackage.EDGE__NETWORK:
				return getNetwork() != null;
			case ArnePackage.EDGE__BASE_LEVELS:
				return baseLevels != null && !baseLevels.isEmpty();
			case ArnePackage.EDGE__START_LEVEL:
				return startLevel != START_LEVEL_EDEFAULT;
			case ArnePackage.EDGE__END_LEVEL:
				return endLevel != END_LEVEL_EDEFAULT;
		}
		return super.eIsSet(featureID);
	}

} //EdgeImpl
